using System;
using Microsoft.SPOT;
using GHIElectronics.NETMF.Hardware;
using Microsoft.SPOT.Hardware;
using System.Threading;
using GHIElectronics.NETMF.FEZ;
using NetMf.CommonExtensions;

namespace AstroX
{
    public static class DS18B20
    {
        public const byte SearchROM = 0xF0;
        public const byte ReadROM = 0x33;
        public const byte MatchROM = 0x55;
        public const byte SkipROM = 0xCC;
        public const byte AlarmSearch = 0xEC;
        public const byte StartTemperatureConversion = 0x44;
        public const byte ReadScratchPad = 0xBE;
        public const byte WriteScratchPad = 0x4E;
        public const byte CopySratchPad = 0x48;
        public const byte RecallEEPROM = 0xB8;
        public const byte ReadPowerSupply = 0xB4;
    }

    public class Thermometer
    {
        private OneWire _DataPin;

        public static float DataToF(long d)
        {
            return (float)(32.0 + (9.0 / 5.0) * (d / 16.0));
        }

        public static float DataToC(long d)
        {
            return (float)(d / 16.0);
        }

        public delegate float UnitConverter(long data);
        

        private byte[][] ids = new byte[][] { new byte[8], new byte[8], new byte[8], new byte[8] };

        public Thermometer(Cpu.Pin pin)
        {
            _DataPin = new OneWire(pin);
            
            //_DataPin.Search_Restart();
            //int i = 0;
            //while (_DataPin.Search_GetNextDevice(ids[i++]))
            //{
            //    Program.Print("Found OneWire thermometer " + i + "...");
            //    StringBuilder sb = new StringBuilder();
                
                
            //}
        }

        public OneWire DataPin
        {
            get { return _DataPin ?? (_DataPin = new OneWire((Cpu.Pin) FEZ_Pin.Digital.Di8)); }
        }

        public float ReadTemp(UnitConverter c)
        {
            return c(ReadTemperatureData());
        }

        public long ReadTemperatureData()
        {
            long data = 0;
            OneWire one = DataPin;
            if (one.Reset())
            {
                one.WriteByte(DS18B20.SkipROM);
                one.WriteByte(DS18B20.StartTemperatureConversion);

                while (one.ReadByte() == 0) {}

                one.Reset();
                one.WriteByte(DS18B20.SkipROM);
                one.WriteByte(DS18B20.ReadScratchPad);

                data = one.ReadByte(); // LSB 
                data |= (ushort)(one.ReadByte() << 8); // MSB
            }
            return data;
        }
    }

    public class ThermometerWatcher
    {
        private readonly Thermometer _Thermometer;
        private readonly TimeSpan _Interval = new TimeSpan(TimeSpan.TicksPerSecond);
        private readonly Thermometer.UnitConverter _Converter = d => d;

        private Timer _Timer;

        private long _Data;
        private float _Temperature;

        public delegate void TemperatureDataChangeDelegate(long from, long to);
        public event TemperatureDataChangeDelegate TemperatureDataChanged;

        public delegate void TemperatureChangeDelegate(float from, float to);
        public event TemperatureChangeDelegate TemperatureChanged;

        public ThermometerWatcher(Thermometer thermometer) : this(thermometer, Thermometer.DataToF) { }

        public ThermometerWatcher(Thermometer thermometer, Thermometer.UnitConverter converter)
        {
            _Thermometer = thermometer;
            _Converter = converter;
        }

        public void Start()
        {
            if (_Timer == null)
            {
                _Timer = new Timer(state =>
                {
                    long data = _Thermometer.ReadTemperatureData();
                    if (_Data != data)
                    {
                        if (TemperatureDataChanged != null)
                            TemperatureDataChanged(_Data, data);
                        _Data = data;
                        float temp = _Converter(data);
                        if (_Temperature != temp)
                        {
                            if (TemperatureChanged != null)
                                TemperatureChanged(_Temperature, temp);
                            _Temperature = temp;
                        }
                    }
                }, this, TimeSpan.Zero, _Interval);
            }
            else _Timer.Change(TimeSpan.Zero, _Interval);
        }

        public void Stop()
        {
            _Timer.Change(0, 0);
        }
    }
}
